package org.luxor.sdk.common.interceptor;


import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okio.Buffer;
import org.luxor.sdk.common.Credential;
import org.luxor.sdk.common.Sign;

import java.io.IOException;
import java.security.SecureRandom;
import java.util.TreeMap;

/**
 * 重试请求
 *
 * @author Mr.yan
 */
public class RetryInterceptor implements Interceptor {

    private final Credential credential;

    /**
     * 延迟
     */
    private final static long delayMillis = 3000;

    /**
     * 叠加延迟
     */
    private final static long increaseDela = 2000;

    /**
     * 日志
     */
    private final TCLogInterceptor log;

    /**
     * 最大重试次数
     */
    private int maxRetry = 3;


    public RetryInterceptor(TCLogInterceptor log, Credential credential) {
        this.log = log;
        this.credential = credential;
    }

    public RetryInterceptor(TCLogInterceptor log, Credential credential, int maxRetry) {
        this.log = log;
        this.credential = credential;
        this.maxRetry = maxRetry;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        RetryWrapper retryWrapper = proceed(chain);
        while (retryWrapper.isNeedReTry()) {
            int retryNum = retryWrapper.getRetryNum() + 1;
            log.debug(String.format("retryNum= %s, url= %s", retryNum, retryWrapper.getRequest().url()));
            try {
                Thread.sleep(delayMillis + retryWrapper.getRetryNum() * increaseDela);
            } catch (InterruptedException e) {
                //.ignore
            }

            retryWrapper.setRetryNum(retryNum);
            retryWrapper.setRequest(formatQueryParams(chain));

            proceed(chain, retryWrapper);
        }
        return retryWrapper.getResponse();
    }

    private RetryWrapper proceed(Chain chain) throws IOException {
        RetryWrapper retryWrapper = new RetryWrapper(chain.request(), maxRetry);
        proceed(chain, retryWrapper);
        return retryWrapper;
    }

    private void proceed(Chain chain, RetryWrapper retryWrapper) throws IOException {
        Response response = chain.proceed(retryWrapper.getRequest());
        retryWrapper.setResponse(response);
    }

    private Request formatQueryParams(Chain chain) throws IOException {
        Buffer buffer = new Buffer();
        if (chain.request().body() != null) {
            chain.request().body().writeTo(buffer);
        }
        byte[] requestPayload = buffer.readByteArray();

        TreeMap<String, String> params = new TreeMap<>();
        HttpUrl url = chain.request().url();
        url.queryParameterNames().forEach(name -> {
            params.put(name, url.queryParameter(name));
        });
        params.remove(Sign.SIGNATURE);
        params.put(Sign.CLIENT_ID, credential.getClientId());
        params.put(Sign.NONCE, String.valueOf(Math.abs(new SecureRandom().nextInt())));
        params.put(Sign.TIMESTAMP, String.valueOf(System.currentTimeMillis() / 1000));
        String signPlainText = Sign.makeSignPlainText(credential.getClientSecret(), params, requestPayload);
        String signature = Sign.sign(credential.getClientSecret(), signPlainText, Sign.SIG_METHOD);
        params.put(Sign.SIGNATURE, signature);

        HttpUrl.Builder urlBuilder = chain.request().url().newBuilder();
        params.forEach(urlBuilder::setQueryParameter);
        return chain.request().newBuilder().url(urlBuilder.build()).build();
    }
}
